package sas

import (
	"gitee.com/micro-plat/sas/sas/modules/const/enum"
	"gitee.com/micro-plat/sas/sas/modules/secret"
	"github.com/micro-plat/lib4go/net"
	"github.com/micro-plat/lib4go/types"
)

// //Response 返回信息
// type Response struct {
// 	UID           string `json:"uid"`
// 	Secret        string `json:"secret"`
// 	PublicSecret  string `json:"pubilc_secret"`
// 	PrivateSecret string `json:"private_secret"`
// 	PkcsType      string `json:"pkcs_type"`
// 	Bits          int    `json:"bits"`
// 	Status        string `json:"status"`
// 	Time          string `json:"time"`
// 	ExpireTime    string `json:"expire_time"`
// }

//SecretResponse 返回信息
type Response struct {
	UID              string `json:"uid"`
	AesSecret        string `json:"aes_secret,omitempty"`
	DesSecret        string `json:"des_secret,omitempty"`
	HmacSecret       string `json:"hmac_secret,omitempty"`
	Md5Secret        string `json:"md5_secret,omitempty"`
	Sha1Secret       string `json:"sha1_secret,omitempty"`
	Sha256Secret     string `json:"sha256_secret,omitempty"`
	Sha512Secret     string `json:"sha512_secret,omitempty"`
	RsaPrivateSecret string `json:"rsa_private_secret,omitempty"`
	RsaPublicSecret  string `json:"rsa_public_secret,omitempty"`
	PkcsType         string `json:"pkcs_type,omitempty"`
	Bits             int    `json:"bits,omitempty"`
	MerchantCert     string `json:"mer_cert,omitempty"`
	SupplierCert     string `json:"spl_cert,omitempty"`
	RootCert         string `json:"root_cert,omitempty"`
	CertKey          string `json:"cert_key,omitempty"`

	Status     string `json:"status"`
	Time       string `json:"time"`
	ExpireTime string `json:"expire_time"`
}

type sasClient struct {
	euid           string
	keyValue       string            //键值连接符  (默认为空)
	chain          string            //每组键值对之间的连接符 (默认为空)
	sort           string            //排序方式 all-密钥和数据都要参与排序,data-只有数据参与排序,static--指定字段参与排序  默认:"data"
	fields         []string          //参与签名验证的字段及其顺序  排序模式为static,需要指定参与排序的字段名,如果不设置,将按照入参所有数据进行排序签名
	sertName       string            //指定密钥键名(默认为空) 字段排序方式为:all,密钥也需要加入原串排序时,必须设置密钥key名称;如果不设置,将只对所有的入参数据进行排序,密钥不会加入排序
	sertKeyValue   string            //密钥键与密钥值连接符(默认为空)  只有设置了密钥key名称才有效
	sertChain      string            //密钥与其它串的连接符(默认为空)
	sertMode       string            //密钥连接方式 head-头部拼接,tail-尾部拼接,headTail-头尾都拼接
	alias          map[string]string //字段别名
	encryptName    []string          //需要加密的字段 只有加入列表的字段名才会被加密/解密
	dynamicKeyName string            //动态密钥名称  默认:secret_id
	lib            secret.ISecret
}

//NewSasClient 初始化sas请求对象
func NewSasClient(euid string, opts ...ClientOption) *sasClient {
	r := sasClient{
		sort:     enum.Data,
		sertMode: enum.Tail,
		euid:     euid,
		alias:    map[string]string{},
		lib:      secret.NewSecret(),
	}
	for _, opt := range opts {
		opt(&r)
	}

	//设置默认密钥字段名
	if r.sort == enum.All && r.sertName == "" {
		r.sertName = "secret"
	}

	return &r
}

func (n *sasClient) buildRaw(input types.XMap, key, signType string) string {

	//若排序为static且配置了fields字段，直接进行原串拼接
	if n.sort == enum.Static && len(n.fields) > 0 {
		return n.spliceKey(n.staticRaw(input), key, signType)
	}

	values := net.NewValues()
	for k, v := range input {
		//空参数不加入签名
		if types.IsEmpty(v) {
			continue
		}
		values.Set(k, types.GetString(v))
	}

	//排序类型为all且配置了密钥名时，密钥将加入排序
	if n.sort == enum.All && n.sertName != "" {
		values.Set(n.sertName, key)
	}

	values.Sort()
	//拼接原串及密钥
	raw := values.Join(n.keyValue, n.chain)
	return n.spliceKey(raw, key, signType)
}

//spliceKey 拼接key
func (n *sasClient) spliceKey(raw, key, signType string) string {
	//若密钥已加入排序，则不再拼接密钥  || rsa签名不能拼接密钥
	if (n.sort == enum.All && n.sertName != "") || signType == RSAStr {
		return raw
	}

	//若配置了密钥名，则将使用相应的连接符进行拼接
	keyStr := key
	if n.sertName != "" {
		keyStr = n.sertName + n.sertKeyValue + keyStr
	}

	//按配置的密钥拼接模式进行密钥拼接
	switch n.sertMode {
	case enum.Head:
		return keyStr + n.sertChain + raw
	case enum.HeadTail:
		return keyStr + n.sertChain + raw + n.sertChain + keyStr
	default:
		return raw + n.sertChain + keyStr
	}
}

//staticRaw 静态原串拼接
func (n *sasClient) staticRaw(input types.XMap) (raw string) {
	//按顺序进行原串拼接
	for k, v := range n.fields {
		value, ok := input[v]
		if !ok || types.IsEmpty(value) {
			continue
		}
		if k == 0 {
			raw = v + n.keyValue + types.GetString(value)
		} else {
			raw = raw + n.chain + v + n.keyValue + types.GetString(value)
		}
	}
	return
}

//获取签名别名
func (n *sasClient) getSignName() string {
	if _, ok := n.alias[enum.SignName]; ok {
		return n.alias[enum.SignName]
	}
	return enum.SignName
}

//获取时间戳别名
func (n *sasClient) getTimespanName() string {
	if _, ok := n.alias[enum.TimestampName]; ok {
		return n.alias[enum.TimestampName]
	}
	return enum.TimestampName
}

//获取euid别名
func (n *sasClient) getEUIDName() string {
	if _, ok := n.alias[enum.EUIDName]; ok {
		return n.alias[enum.EUIDName]
	}
	return enum.EUIDName
}

//获取动态密钥别名
func (n *sasClient) getDynamicKeyName() string {
	if _, ok := n.alias[enum.DynamicKeyName]; ok {
		return n.alias[enum.DynamicKeyName]
	}
	return enum.DynamicKeyName
}

//checkType 检查类型是否正确
func (n *sasClient) checkType(signType, encryptType string, authType int) (err error) {

	//检查签名类型
	if err = enum.CheckSignType(signType, authType); err != nil {
		return
	}

	if authType == enum.Single {
		return
	}

	//检查加密类型
	if err = enum.CheckEncryptType(encryptType, authType); err != nil {
		return
	}

	return
}
